/**
 * Copyright (c) 2023 murenchao
 * taomu is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.utils

import com.google.common.collect.HashBasedTable
import com.google.common.collect.Table
import java.io.File
import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.Target
import java.net.URL
import java.nio.charset.Charset
import java.util.Enumeration
import java.util.Objects
import org.apache.commons.lang3.ObjectUtils
import org.slf4j.LoggerFactory

class ServiceLoader {
	val static LOG = LoggerFactory.getLogger(ServiceLoader);
	val PREFIX = "META-INF/services/";
	val Table<String, String, Class<?>> providers = HashBasedTable.create();

	@Documented
	@Retention(RUNTIME)
	@Target(#[TYPE, FIELD])
	annotation Spi {
		String value;
		Class<?> svc = Void;
		boolean singleton = false;
	}

	new() {
	}

	def loader(Class<?> svc) {
		return this.loader(svc, Thread.currentThread.contextClassLoader);
	}

	def loader(Class<?> svc, ClassLoader cloader) {
		val service = Objects.requireNonNull(svc, "class 不能为 null");
		val loader = ObjectUtils.defaultIfNull(cloader, ClassLoader.getSystemClassLoader());
		val fullName = PREFIX + service.name;
		LOG.info("fullName : {}", fullName);
		var Enumeration<URL> configs;
		if (loader === null) {
			configs = ClassLoader.getSystemResources(fullName);
		} else {
			configs = loader.getResources(fullName);
		}
		LOG.info("Tests if this enumeration contains more elements. {}", configs.hasMoreElements);
		while (configs.hasMoreElements) {
			var path = configs.nextElement.toString;
			LOG.info(path);
			path = path.replace("file:", "")
			var strs = org.apache.commons.io.FileUtils.readFileToString(new File(path), Charset.forName("UTF-8"));
			strs.split("\n").filterNull.forEach [
				try {
					var zlass = Class.forName(it, false, loader);
					var alias = zlass.getAnnotation(Spi);
					if (alias !== null && !alias.value.equals("")) {
						providers.put(service.name, alias.value, zlass);
					}
					providers.put(service.name, zlass.simpleName, zlass);
				} catch (ClassNotFoundException ex) {
					LOG.info("类 " + it + " 不存在", ex)
				}
			];
		}
		return this;
	}

	def add(Class<?> svc, String name, Class<?> zlass) {
		providers.put(svc.name, name, zlass);
		return this;
	}

	def getBean(Class<?> svc, String name) {
		if (!providers.containsRow(svc.name)) {
			this.loader(svc);
		}
		return providers.get(svc.name, name);
	}

	def getBean(Class<?> svc, int index) {
		if (!providers.containsRow(svc.name)) {
			this.loader(svc);
		}
		LOG.info("service loader providers size : {}", providers.size);
		return providers.row(svc.name).values.get(index);
	}
}
